package enemy;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;

import player.Player;
import utility.Util;
import drawables.EnemyDrawable;
import enemy.actions.Action;
import enemy.states.Patrol;
import enemy.states.Shooting;
import enemy.states.Sighting;
import enemy.states.State;
import game.AudioEngine;
import game.Camera;
import game.Level;

public class Enemy {
	
	private float x;
	private float y;
	private float rotation; //in degrees
	
	private List<State> states;
	private State currentState;
	
	private EnemyDrawable drawable;
	
	private float sightedness = 0.0f;
	private float ave = 0.0f;
	
	private float dist = 0f;
	
	public Enemy(float x, float y, float rotation) throws SlickException {
		states = new ArrayList<State>();
		this.x = x;
		this.y = y;
		this.rotation = rotation;
		
		states.add(new Patrol(this));
		states.add(new Sighting(this));
		states.add(new Shooting());
		currentState = states.get(0);
		
		drawable = new EnemyDrawable();
	}
	
	public void addAction(Action a) {
		((Patrol)states.get(0)).addAction(a);
	}
	
	public float getRotation() {
		return rotation;
	}
	
	public void setRotation(float rotation) {
		
		this.rotation = rotation;
	}

	
	public void doAction(Level level) {
		currentState.doAction(level);
	}
	
	public void update() {
		drawable.update(this);
		
		if(sightedness > 0f && sightedness < 1f)
			AudioEngine.playCharge(sightedness);

	}
	
	public void draw(Graphics g, Camera camera){
		drawable.draw(g, camera);
//		g.drawString(currentState+"", 50, 50);
	}
	
	public void setPatrol() {
		currentState = states.get(0);
	}
	
	public void setSighting(Player p) {
		currentState = states.get(1);
		((Sighting)currentState).setPlayer(p);
	}
	
	public void setShooting(Player p) {
		currentState = states.get(2);
		((Shooting)currentState).setPlayer(p);
	}
	
	/**
	 * turnRight, stops if we reach the required rotation
	 * @param x
	 * @param y
	 */
	public boolean turnRight(float rotation, float speed) {
		
		if(Math.abs(rotation-this.rotation)<speed) {
			this.rotation = rotation;
			return true;
		}
		this.rotation+=(speed);
		
		this.rotation = Util.scale(this.rotation) % 360f;
		
		if(rotation < 0)
			rotation += 360f;
		
		return false;
		
	}
	
	/**
	 * turns left, stops if we reach the required rotation
	 * @param rotation
	 */
	public boolean turnLeft(float rotation, float speed) {

		if(Math.abs(rotation-this.rotation)<speed) {
			this.rotation = rotation;
			return true;
		}
		this.rotation-=(speed);
		

		this.rotation = Util.scale(this.rotation) % 360f;
		
		if(rotation < 0)
			rotation += 360f;
		
		return false;
	}
	
	public void turnLeft() {
		this.rotation -= ROTATION_STEP;
		
		this.rotation = Util.scale(this.rotation) % 360f;
		
		if(rotation < 0)
			rotation += 360f;
	}
	
	public void turnRight() {
		this.rotation += ROTATION_STEP;
		

		this.rotation = Util.scale(this.rotation) % 360f;
		
		if(rotation < 0)
			rotation += 360f;
	}
	
	/**
	 * moves forward, stops if we hit the required location, returns true if we reached it...
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean move(float x, float y) {
		//TODO
		
		float dx = Math.abs(x - this.x);
		float dy = Math.abs(y - this.y);
		
		if(dx<MOVEMENT_STEP*1.1f && dy<MOVEMENT_STEP*1.1f) {
			this.x = x;
			this.y = y;
			return true;
		}
		
		dy = (float) (MOVEMENT_STEP*Math.cos(Math.toRadians(rotation)));
		dx = (float) (MOVEMENT_STEP*Math.sin(Math.toRadians(rotation)));
		if(dy < 0.01f && dy > -0.01f) dy = 0f;
		if(dx < 0.01f && dx > -0.01f) dx = 0f;
		
		this.x += dx;
		this.y -= dy;
		
		return false;
	}
	
	public void setAve(float x) {
		this.ave = x;
	}
	
	public void turnToFace(float x, float y) {
		
		float target = getTargetRotation(x, y); //angle we want to be at (0-359)

		float ticksTillDeath = 1/ave;
		
		float current = getRotation();
		
		if(target != Math.round(current)){ //not facing it
			//turn to face it
			if(getDirection(target)) {
				
				//we are turning right
				float degreesLeft = target-current;
				
				float ratio = degreesLeft / ticksTillDeath;
				
				turnRight(target, ratio*1.5f);
			} else {
				
				//we are turning left
				float degreesLeft = Math.abs(target-360);
				
				float ratio = degreesLeft / ticksTillDeath;
				
				turnLeft(target, ratio*1.5f);
			}
		}
	}
	
	/**
	 * should we turn left or right
	 * @param rotation
	 * @return true if right
	 */
	public boolean getDirection(float rotation) {
		
		this.rotation = (this.rotation+360f) % 360f;
		
		float diff = rotation - this.rotation;
		if((diff < 0f && diff >= -180f) || diff > 180)
			return false;
		else
			return true;
		
	}

	/**
	 * Rotation we want to be at, based on player location and target location
	 * @param loc
	 * @param x
	 * @param y
	 * @return
	 */
	public float getTargetRotation(float x, float y) {
		
		float dy = this.y - y;
		float dx = x - this.x;
		
		double atan = Math.atan2(dx, dy);
		
		float degrees = (float) (Math.toDegrees(atan));
		
		return (degrees<0)? ((float)(degrees+360)):degrees;

	}
	
	
	public static final String DIR = Util.resources+"enemy"+File.separator;
	
	//public static float STEP = 1.0f;
	
	public static float ROTATION_STEP = 1.0f;
	public static float MOVEMENT_STEP = 1.5f;
	
	public static float MAX_VISION = 300f;
	public static float FOV = 60;

	public float getX() {
		return x;
	}

	public float getY() {
		return y;
	}

	public float getSightedness() {
		return sightedness;
	}
	
	public void setSightedness(float sightedness) {
		this.sightedness = sightedness;
	}

	public void setDist(float dist) {
		this.dist = dist;
	}
	
	public float distanceToPlayer() {
		return dist;
	}
	
}
